package Tests
{
	import Box2D.Collision.Shapes.b2CircleShape;
	import Box2D.Collision.Shapes.b2EdgeShape;
	import Box2D.Collision.Shapes.b2PolygonShape;
	import Box2D.Common.Math.b2Vec2;
	import Box2D.Common.Math.b2Vec2Array;
	import Box2D.Dynamics.Joints.b2RevoluteJointDef;
	import Box2D.Dynamics.Joints.b2WheelJoint;
	import Box2D.Dynamics.Joints.b2WheelJointDef;
	import Box2D.Dynamics.b2Body;
	import Box2D.Dynamics.b2BodyDef;
	import Box2D.Dynamics.b2BodyType;
	import Box2D.Dynamics.b2FixtureDef;
	
	import Framework.StringUtil;
	import Framework.Test;
	
	public class Car extends Test
	{
		private var m_car:b2Body;
		private var m_wheel1:b2Body;
		private var m_wheel2:b2Body;
		
		private var m_hz:Number;
		private var m_zeta:Number;
		private var m_speed:Number
		private var m_spring1:b2WheelJoint;
		private var m_spring2:b2WheelJoint;
		
		public function Car()
		{
			super();
			m_hz = 4.0;
			m_zeta = 0.7;
			m_speed = 50.0;
			createGround();
			createTeeter();
			createBridge();
			createBoxes();
			createCar();
		}
		private function createGround():void{			
			var shape:b2EdgeShape = new b2EdgeShape;
			
			var fd:b2FixtureDef = new b2FixtureDef;
			fd.shape = shape;
			fd.density = 0.0;
			fd.friction = 0.6;
			
			shape.Set(new b2Vec2(-20.0, 0.0),new b2Vec2(20.0, 0.0));
			m_groundBody.CreateFixture(fd);
			
			var hs:Array = [0.25, 1.0, 4.0, 0.0, 0.0, -1.0, -2.0, -2.0, -1.25, 0.0];
			
			var x:Number = 20.0;
			var y1:Number = 0.0;
			var dx:Number = 5.0;
			
			for (var i:int = 0; i < 10; ++i)
			{
				var y2:Number = hs[i];
				shape.Set(new b2Vec2(x, y1),new b2Vec2(x + dx, y2));
				m_groundBody.CreateFixture(fd);
				y1 = y2;
				x += dx;
			}
			
			for ( i = 0; i < 10; ++i)
			{
				y2 = hs[i];
				shape.Set(new b2Vec2(x, y1), new b2Vec2(x + dx, y2));
				m_groundBody.CreateFixture(fd);
				y1 = y2;
				x += dx;
			}
			
			shape.Set( new b2Vec2(x, 0.0),new b2Vec2(x + 40.0, 0.0));
			m_groundBody.CreateFixture(fd);
			
			x += 80.0;
			shape.Set( new b2Vec2(x, 0.0),new  b2Vec2(x + 40.0, 0.0));
			m_groundBody.CreateFixture(fd);
			
			x += 40.0;
			shape.Set(new b2Vec2(x, 0.0),new  b2Vec2(x + 10.0, 5.0));
			m_groundBody.CreateFixture(fd);
			
			x += 20.0;
			shape.Set(new b2Vec2(x, 0.0), new b2Vec2(x + 40.0, 0));
			m_groundBody.CreateFixture(fd);
			
			x += 40.0;
			shape.Set(new b2Vec2(x, 0.0), new b2Vec2(x, 20.0));
			m_groundBody.CreateFixture(fd);
		}
		private function createTeeter():void{
			var bd:b2BodyDef = new b2BodyDef;
			bd.position.Set(140.0, 1.0);
			bd.type = b2BodyType.b2_dynamicBody;
			var body:b2Body = m_world.CreateBody(bd);
			
			var box:b2PolygonShape = new b2PolygonShape;
			box.SetAsBox(10.0, 0.25);
			body.CreateFixtureByShape(box, 1.0);
			
			var jd:b2RevoluteJointDef = new b2RevoluteJointDef;
			jd.Initialize(m_groundBody, body, body.GetPosition());
			jd.lowerAngle = -8.0 * Math.PI / 180.0;
			jd.upperAngle = 8.0 * Math.PI / 180.0;
			jd.enableLimit = true;
			m_world.CreateJoint(jd);
			
			body.ApplyAngularImpulse(100.0);			
		}
		private function createBridge():void{
			var N:int = 20;
			var shape:b2PolygonShape = new b2PolygonShape;
			shape.SetAsBox(1.0, 0.125);
			
			var fd:b2FixtureDef = new b2FixtureDef;
			fd.shape = shape;
			fd.density = 1.0;
			fd.friction = 0.6;
			
			var jd:b2RevoluteJointDef = new b2RevoluteJointDef;
			
			var prevBody:b2Body = m_groundBody
			for (var i:int = 0; i < N; ++i)
			{
				var bd:b2BodyDef = new b2BodyDef;
				bd.type = b2BodyType.b2_dynamicBody;
				bd.position.Set(161.0 + 2.0 * i, -0.125);
				var body:b2Body = m_world.CreateBody(bd);
				body.CreateFixture(fd);
				
				var anchor:b2Vec2 = new b2Vec2(160.0 + 2.0 * i, -0.125);
				jd.Initialize(prevBody, body, anchor);
				m_world.CreateJoint(jd);
				
				prevBody = body;
			}
			
			anchor = new b2Vec2(160.0 + 2.0 * N, -0.125);
			jd.Initialize(prevBody, m_groundBody, anchor);
			m_world.CreateJoint(jd);			
		}
		private function createBoxes():void{
			var box:b2PolygonShape = new b2PolygonShape;
			box.SetAsBox(0.5, 0.5);
			
			var body:b2Body = null;
			var bd:b2BodyDef = new b2BodyDef;
			bd.type = b2BodyType.b2_dynamicBody;
			
			bd.position.Set(230.0, 0.5);
			body = m_world.CreateBody(bd);
			body.CreateFixtureByShape(box, 0.5);
			
			bd.position.Set(230.0, 1.5);
			body = m_world.CreateBody(bd);
			body.CreateFixtureByShape(box, 0.5);
			
			bd.position.Set(230.0, 2.5);
			body = m_world.CreateBody(bd);
			body.CreateFixtureByShape(box, 0.5);
			
			bd.position.Set(230.0, 3.5);
			body = m_world.CreateBody(bd);
			body.CreateFixtureByShape(box, 0.5);
			
			bd.position.Set(230.0, 4.5);
			body = m_world.CreateBody(bd);
			body.CreateFixtureByShape(box, 0.5);			
		}
		private function createCar():void{
			var chassis:b2PolygonShape = new b2PolygonShape;
			var vertices:b2Vec2Array = new b2Vec2Array(8);
			vertices.setAt2(0,-1.5, -0.5);
			vertices.setAt2(1,1.5, -0.5);
			vertices.setAt2(2,1.5, 0.0);
			vertices.setAt2(3,0.0, 0.9);
			vertices.setAt2(4,-1.15, 0.9);
			vertices.setAt2(5,-1.5, 0.2);
			chassis.Set(vertices, 6);
			
			var circle:b2CircleShape = new b2CircleShape;
			circle.radius = 0.4;
			
			var bd:b2BodyDef = new b2BodyDef;
			bd.type = b2BodyType.b2_dynamicBody;
			bd.position.Set(0.0, 1.0);
			m_car = m_world.CreateBody(bd);
			m_car.CreateFixtureByShape(chassis, 1.0);
			
			var fd:b2FixtureDef = new b2FixtureDef;
			fd.shape = circle;
			fd.density = 1.0;
			fd.friction = 0.9;
			
			bd.position.Set(-1.0, 0.35);
			m_wheel1 = m_world.CreateBody(bd);
			m_wheel1.CreateFixture(fd);
			
			bd.position.Set(1.0, 0.4);
			m_wheel2 = m_world.CreateBody(bd);
			m_wheel2.CreateFixture(fd);
			
			var jd:b2WheelJointDef = new b2WheelJointDef;
			var axis:b2Vec2 = new b2Vec2(0.0, 1.0);
			
			jd.Initialize(m_car, m_wheel1, m_wheel1.GetPosition(), axis);
			jd.motorSpeed = 0.0;
			jd.maxMotorTorque = 20.0;
			jd.enableMotor = true;
			jd.frequencyHz = m_hz;
			jd.dampingRatio = m_zeta;
			m_spring1 = m_world.CreateJoint(jd) as b2WheelJoint;

			jd.Initialize(m_car, m_wheel2, m_wheel2.GetPosition(), axis);
			jd.motorSpeed = 0.0;
			jd.maxMotorTorque = 10.0;
			jd.enableMotor = false;
			jd.frequencyHz = m_hz;
			jd.dampingRatio = m_zeta;
			m_spring2 = m_world.CreateJoint(jd) as b2WheelJoint;	
		}
		public override function get hotKeyNote():String{
			return 'Keys: left = a, brake = s, right = d, hz down = q, hz up = e';
		}
		public override function Key(key:String):void
		{
			switch (key)
			{
				case 'a':
					m_spring1.SetMotorSpeed(m_speed);
					break;
				
				case 's':
					m_spring1.SetMotorSpeed(0.0);
					break;
				
				case 'd':
					m_spring1.SetMotorSpeed(-m_speed);
					break;
				
				case 'q':
					m_hz = Math.max(0.0, m_hz - 1.0);
					m_spring1.SetSpringFrequencyHz(m_hz);
					m_spring2.SetSpringFrequencyHz(m_hz);
					break;
				
				case 'e':
					m_hz += 1.0;
					m_spring1.SetSpringFrequencyHz(m_hz);
					m_spring2.SetSpringFrequencyHz(m_hz);
					break;
			}
		}
		public override function Step():void{
			writeMessage(StringUtil.sprintf('frequency = %.3f hz, damping ratio = %.3f',m_hz,m_zeta));
			cameraX = m_car.GetPosition().x;
			super.Step();
		}
	}
}